/**
@author 迷途小羔羊
2022.09.27
*/
module GYLite {
	export class AtlasRender implements IResource{
		public static defaultGap:number = 1;
		public static defaultAtlasName:string = "GYLITE_ATLAS";
		public static _instance:AtlasRender;
		public static getInstance():AtlasRender
		{
			if(AtlasRender._instance == null)
				AtlasRender._instance = new AtlasRender(egret.web.WebGLRenderContext.getInstance(0, 0), 1024, AtlasRender.defaultGap);
			return AtlasRender._instance;
		}
		/**获取batch纹理，如果存在，如果不存在则尝试进行合批创建*/
		public static getBatchTexture(tex:egret.Texture,display:IBatch):egret.Texture
		{
			if(tex == null)
				return null;
			if(tex.$bitmapData == null || tex.$bitmapData.$source == null)
			{
				// console.warn(SysError.BATCH_SOURCE_LOST.throwError([tex.hashCode]));
				return tex;
			}
			let batchInfo:BatchInfo;
			batchInfo = AtlasRender.getInstance().addBatch(tex,display);
			if(batchInfo)
				batchInfo.measureAndDraw();
			return batchInfo?batchInfo.$batchTexture:tex;
		}
		public $disposed:boolean;
		protected _rectAtlasMgr:AtlasManager;		
		protected _atlasDict:any;
		protected _gap:number;
		protected _webglRenderContext: egret.sys.RenderContext;
		/**
		 * @param webglRenderContext 绘图上下文
		 * @param maxSize 加入图集的最大尺寸限制
		 * @param gap 小图填充之间的间隙
		*/
		public constructor(webglRenderContext: egret.sys.RenderContext, maxSize: number, gap: number) 
		{
			let s= this;
			s._atlasDict = {};
			s._gap = gap;
			AtlasRect.maxSize = maxSize;
			s._rectAtlasMgr = new AtlasManager();
			s._webglRenderContext = webglRenderContext;
			PoolUtil.setPoolLengthWarn(TextTexture, 10000);
		}
		public get atlasNum():number
		{
			return this._rectAtlasMgr.atlasNum;
		}		
		public getRectAtlasMgr():AtlasManager
		{
			return this._rectAtlasMgr;
		}
		/**移除合批到图集的小图		 
		 * @param batchInfo 合批记录
		*/
		public removeBatch(batchInfo:BatchInfo):void
		{
			let s= this;			
			if(batchInfo)
			{
				let atlas:Atlas;		
				let texName:string;
				texName = batchInfo.atlasInfo.rectInfo.texName;						
				atlas = batchInfo.atlas;
				atlas.removeSubRes(texName);
				s._rectAtlasMgr.removeTexture(texName);
			}			
		}
		/**源小图合批到图集，会对tex进行合批操作，由此tex会产生batchInfo属性，代表已经合批
		 * @param tex 源小图纹理		 
		 * @param display 申请合批的显示对象(需实现IBatch接口)
		 * @return 成功合批返回合批信息 BatchInfo，否则返回null
		*/
		public addBatch(tex:egret.Texture, display:IBatch, releaseFree:boolean=true):BatchInfo
		{
			let s = this;
			let result:{atlasRect:AtlasRect,atlasInfo:AtlasInfo,error:SysError}
			let atlas:Atlas;
			let batchInfo:BatchInfo;
			let atlasName:string;
			let drawParam:BatchDrawParam;
			if(tex == null)return null;			
			if(tex.$getTextureWidth() > AtlasRect.maxSize || tex.$getTextureHeight() > AtlasRect.maxSize)
				return null;			
			batchInfo = tex.batchManager.getBatchByDisplay(display);			
			//存在atlasName图集上的BatchInfo，则在batchManger记录display对batchInfo的引用即可
			if(batchInfo)
			{
				tex.$batchManager.addDisplay(display,batchInfo);					
				return batchInfo;				
			}
			atlasName = display.getBatchAtlasName();
			drawParam = display.getBatchDrawParam();
			//给虚拟图集添加小图（没有则创建新的）			
			if(drawParam == null)
				result = s._rectAtlasMgr.addTexture(tex.$hashCode+"",atlasName,tex.textureWidth,tex.textureHeight,1,s._gap);			
			else
			{
				if(drawParam.type == BatchDrawType.TEXT || drawParam.type == BatchDrawType.GRAPHICS)
					result = s._rectAtlasMgr.addTexture(tex.$hashCode+"",atlasName,tex.textureWidth,tex.textureHeight,1,s._gap);
				else
					result = s._rectAtlasMgr.addTexture(tex.$hashCode+"",atlasName,drawParam.getWidth(),drawParam.getHeight(),1,s._gap);
				if(result.atlasInfo)
					result.atlasInfo.rectInfo.drawParam = drawParam;
			}
			if(result.error)//图集不位置不够，插入失败，尝试释放空闲的合批
			{
				atlas = s.getLastAtlas(atlasName);
				if(atlas && atlas.$freeCount > 0 && releaseFree)
				{
					atlas.releaseFreeBatch();
					return s.addBatch(tex, display, false);					
				}
				console.warn(result.error.msg);
				return null;
			}
			//给实体图集添加小图（没有则创建新的）
			let atlasInfo:AtlasInfo;
			atlasInfo = result.atlasInfo;
			if(s._atlasDict[atlasInfo.atlasId] == null)
				s._atlasDict[atlasInfo.atlasId] = new Atlas(result.atlasRect,s._webglRenderContext);			
			atlas = s._atlasDict[atlasInfo.atlasId];
			let resObj:ResObject = atlas.addSubRes(atlasInfo);
			return tex.$batchManager.addBatch(display,atlasInfo,atlas,resObj);
		}
		
		/**创建一张图集
		 * @param width 宽度
		 * @param height 高度
		 * @param atlasName 图集名称
		 * @param createSpriteSheet 图集配置，默认true，则创建空图集，当读取外部图集则认为是非空，可以设置为false，再调用AtlasRender.inputSpriteSheet方法导入配置
		 * @param source 图集数据源
		*/
		public createAtlas(width:number,height:number,atlasName:string,source:HTMLCanvasElement|HTMLImageElement|HTMLVideoElement=null,createSpriteSheet:boolean=true):{atlas:Atlas,atlasRect:AtlasRect,error:SysError}
		{
			let s = this;
			let result:{atlas:Atlas,atlasRect:AtlasRect,error:SysError};
			let atlas:Atlas;
			let rect:AtlasRect;
			//创建虚拟图集
			result = <any>s._rectAtlasMgr.createAtlas(width,height,atlasName,null,true);
			if(result.error)			
				return result;			
			//创建实体图集
			rect = result.atlasRect;
			if(!createSpriteSheet)//如不创建图集配置表，则认为外部存在配置，图集非空则清空图集的空白区域
				rect.emptyRects.length = 0;
			if(s._atlasDict[rect.atlasId] == null)
				s._atlasDict[rect.atlasId] = new Atlas(rect,s._webglRenderContext,source,createSpriteSheet);
			atlas = s._atlasDict[rect.atlasId];
			result.atlas = atlas;
			return result;
		}
		/**移除图集纹理
		 * @param atlasName 图集名称
		*/
		public removeAtlas(atlasName:string):void
		{
			let s= this;
			let atlas:Atlas;
			for(var key in s._atlasDict)
			{
				atlas = s._atlasDict[key];
				if(atlas.atlasName == atlasName)
				{
					atlas.dispose();
					delete s._atlasDict[key]
				}
			}
		}
		/**导入图集小图配置
		 * @param spriteSheet 图集配置
		 * @param atlasId 图集id
		*/
		public inputSpriteSheet(spriteSheet:ResObject,atlasId:number):void
		{
			let atlas:Atlas;			
			let s= this;
			let frames:any;
			let lines:Line[],resultLines:Line[];	
			let tx:number,ty:number;
			frames = spriteSheet.res.frames;
			atlas = s._atlasDict[atlasId];
			lines = [];
			resultLines = [new Line(new Point(0,0),new Point(atlas.width,0))];
			atlas.createSpriteSheep(spriteSheet);
			for (var subkey in frames) {
				var config:any = frames[subkey];				
				if (config.scale9grid) {
					var str:string = config.scale9grid;
					var list:string[] = str.split(",");
					config.scale9grid = new egret.Rectangle(parseInt(list[0]), parseInt(list[1]), parseInt(list[2]), parseInt(list[3]));
				}
				tx = config.x + config.offX;
				ty = config.y + config.offY + config.h;				
				lines.push(new Line(new Point(tx,ty),new Point(tx + config.w,ty)));
				atlas.addSubResByConfig(subkey, config);
				//todo
				//此处应把纹理添加到虚拟图集中，以便可以反向完整输出数据，因为缺少offX offY w h(排除透明的定位和宽高)的设计，先不写
			}			
			//生成空闲区域
			let i:number,len:number;
			let j:number,len2:number;
			let l1:Line,l2:Line;			
			let minusArr:Line[] = [];
			let cont:boolean;
			len = lines.length;			
			for(i=0;i<len;++i)
			{
				l1 = lines[i];				
				len2 = resultLines.length;				
				for(j=0;j<len2;)
				{
					cont = false;
					l2 = resultLines[j];								
					Line.minusX(l2,l1,minusArr);
					if(minusArr.length == 0)
					{
						++j;
						continue;
					}
					
					if(l1 == minusArr[minusArr.length - 1])
					{
						minusArr.pop();
						cont = true;
					}					
					if(minusArr.length == 0)
						resultLines.splice(j,1);					
					else if(minusArr.length == 1)
					{
						resultLines.splice(j,1,minusArr[0]);
						++j;
					}						
					else if(minusArr.length == 2)					
					{
						resultLines.splice(j,1,minusArr[0],minusArr[1]);
						j+=2;
					}						
					else if(minusArr.length == 3)
					{
						resultLines.splice(j,1,minusArr[0],minusArr[1],minusArr[2]);
						j+=3;
					}											
					len2 = resultLines.length;
					if(!cont)
						break;
				}								
			}			
			let temp:number;		
			let min:number = 16,hMin:number=16,wMin:number=12;
			len = resultLines.length;
			for(i=0;i<len;)
			{
				l1 = resultLines[i];
				l1.setBorder(NaN,atlas.height);				
				l2 = resultLines[i + 1];				
				if(l2 == null)
				{
					if(l1.lengthX >= wMin && l1.height >= atlas.height)
						atlas.atlasRect.addEmptyRect(l1.convertToAtalsRect(atlas.atlasRect));					
					break;
				}					
				min = Math.min(40,400/l1.lengthX|0);//根据宽度大小设定高度距离差为多少合并
				temp = l1.a.y - l2.a.y;
				if(l1.height < hMin)
				{
					resultLines.splice(i, 1);
					--len;
				}
				else if(temp >= 0 && (temp <= min || l2.lengthX < wMin))
				{
					l1.b.x = l2.b.x;
					resultLines.splice(i + 1, 1);
					--len;
				}
				else if(temp < 0 && (temp > -min || l2.lengthX < wMin))
				{
					l1.b.x = l2.b.x;
					l1.a.y = l1.b.y = l2.a.y;
					resultLines.splice(i + 1, 1);
					--len;
				}		
				else if(l1.lengthX < wMin)
				{
					resultLines.splice(i, 1);
					--len;
				}
				else									
				{
					atlas.atlasRect.addEmptyRect(l1.convertToAtalsRect(atlas.atlasRect));
					++i;
				}
			}
			//测试代码
			// let sp:egret.Sprite;
			// sp = new egret.Sprite;
			// let g:egret.Graphics;
			// g = sp.graphics;
			// g.lineStyle(1,0xff00ff);
			// len = atlas.atlasRect.emptyRects.length;
			// for(i=0;i<len;++i)
			// {
			// 	l1 = resultLines[i];
			// 	g.moveTo(l1.a.x,l1.a.y);
			// 	g.lineTo(l1.right_a_x,l1.right_a_y);
			// 	g.lineTo(l1.right_b_x,l1.right_b_y);
			// 	g.lineTo(l1.bottom_a_x,l1.bottom_a_y);
			// 	g.lineTo(l1.a.x,l1.a.y);
			// }
			
			// sp.x = 1;
			// sp.y = 120;
			// GYLite.GYSprite.stage.addChild(sp);
		}

		public dispose():void
		{
			let s= this;
			if(s.$disposed)return;
			s.$disposed = true;
			for(let key in s._atlasDict)			
				s._atlasDict[key].dispose();			
			s._atlasDict = null;
		}
		public get disposed():boolean
		{
			return this.$disposed;
		}
		/**获取最近产生的一张名为atlasName的图集*/
		public getLastAtlas(atlasName:string):Atlas
		{
			let len:number;
			let s = this;
			let atlas:Atlas;
			len = s._rectAtlasMgr.atlasNum;
			while(--len > -1)
			{
				atlas = s._atlasDict[len];
				if(atlas.atlasName == atlasName)
					return atlas;
			}
			return atlas;
		}
	}
	export class Point{
		public x:number;
		public y:number;
		constructor(x:number,y:number)
		{
			this.x = x;
			this.y = y;
		}
	}
	export class Line{
		public static minusX(srcL:Line,minusL:Line,result:Line[]=null):Line[]
		{
			let temp:number;
			if(result == null)
				result = [];
			else
				result.length = 0;			
			if(minusL.a.x >= srcL.b.x || minusL.b.x <= srcL.a.x)
				return result;
			if(minusL.a.x <= srcL.a.x)
			{
				if(minusL.b.x >= srcL.b.x)
				{//减数包含源数
					if(minusL.a.y < srcL.a.y)
					{//减数被减去中间一段
						if(srcL.a.x > minusL.a.x)
							result.push(new Line(new Point(minusL.a.x,minusL.a.y),new Point(srcL.a.x,minusL.a.y)));	
						if(srcL.b.x < minusL.b.x)
						{
							minusL.a.x = srcL.b.x;
							result.push(srcL,minusL);
						}
						else
							result.push(srcL);
						
					}
					else
					{//源数减去减数的左边，并且设置y值跟减数一样
						srcL.a.y = srcL.b.y = minusL.a.y;
						srcL.a.x = minusL.a.x;
						minusL.a.x = srcL.b.x;
						if(minusL.a.x < minusL.b.x)
							result.push(srcL, minusL);
						else
							result.push(srcL);
					}					
				}
				else
				{//减数与源数的左边有交集
					if(minusL.a.y < srcL.a.y)
					{//减数被源数减去右边
						if(minusL.a.x < srcL.a.x)
						{
							minusL.b.x = srcL.a.x;
							result.push(minusL,srcL);
						}
						else						
							result.push(srcL);
					}
					else if(minusL.a.y > srcL.a.y)
					{//减数减去源数的左边						
						srcL.a.x = minusL.b.x;
						result.push(minusL,srcL);
					}
					else
					{//合并
						srcL.a.x = minusL.a.x;
						result.push(srcL);
					}
				}
			}
			else if(minusL.a.x > srcL.a.x)
			{
				if(minusL.b.x <= srcL.b.x)
				{//源数包含减数
					if(minusL.a.y <= srcL.a.y)
					{//减数被覆盖掉						
						result.push(srcL);
					}
					else
					{//源数被减去中间一段
						temp = srcL.b.x - minusL.b.x;	
						srcL.b.x = minusL.a.x;
						result.push(srcL,minusL);												
						if(temp > 0)					
							result.push(new Line(new Point(minusL.b.x,srcL.a.y),new Point(minusL.b.x+temp,srcL.a.y)));
					}
				}
				else
				{//减数与源数右边有交集
					if(minusL.a.y < srcL.a.y)
					{//源数减去减数左边
						minusL.a.x = srcL.b.x;
						result.push(srcL,minusL);
					}
					else
					{//减数减去源数的右边												
						srcL.b.x = minusL.a.x;
						result.push(srcL,minusL);
					}
				}
			}	
			return result;		
		}
		public a:Point;
		public b:Point;
		private _borderW:number;
		private _borderH:number;
		constructor(p1:Point,p2:Point)
		{
			let s= this;
			s.a = p1;
			s.b = p2;			
			s._borderH = s._borderW = NaN;
		}
		public setBorder(w:number,h:number):void
		{
			this._borderW = w;
			this._borderH = h;
		}
		public get right_b_x():number
		{
			return this.bottom_b_x;
		}
		public get right_b_y():number
		{
			return this.bottom_b_y;
		}
		public get right_a_x():number
		{
			let s = this;
			return s.bottom_b_x;
		}
		public get right_a_y():number
		{
			return this.a.y;
		}
		public get bottom_a_x():number
		{			
			return this.a.x;
		}
		public get bottom_a_y():number
		{
			let s= this;
			return s._borderH;
		}
		public get bottom_b_x():number
		{
			let s= this;
			return s._borderW == s._borderW?s._borderW:s.b.x;
		}
		public get bottom_b_y():number
		{
			let s= this;
			return s._borderH==s._borderH?s._borderH:10;
		}
		public get lengthX():number
		{
			let s= this;
			return s.a.x > s.b.x?s.a.x - s.b.x:s.b.x - s.a.x;
		}
		public get height():number
		{
			let s = this;
			return s._borderH - s.a.y;
		}
		public convertToAtalsRect(parent:AtlasRect):AtlasRect
		{
			let s= this;
			return AtlasRect.createAtlasRect(s.a.x,s.b.y,s.right_a_x - s.a.x,s.bottom_b_y - s.b.y,parent);			
		}		
	}
}